/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright (c) 2008-2009, The KiWi Project (http://www.kiwi-project.eu)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 * - Neither the name of the KiWi Project nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Contributor(s):
 * 
 * 
 */
package kiwi.core.model.rdf;

import kiwi.core.model.Constants;
import kiwi.core.model.user.KiWiUser;
import kiwi.core.services.triplestore.TripleStoreUtil;
import org.hibernate.Hibernate;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Index;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Locale;

/**
 * KiWiLiterals store literal information from the knowledge base. They directly 
 * correspond to an RDF literal stored in Sesame. KiWiLiterals are
 * parametrized with the Java type corresponding to the literal content they store.
 * The method getType() returns the KiWiResource representing the RDF type of this 
 * literal. This information can e.g. be used to provide appropriate user interfaces.
 * 
 * A factory class should be provided that maps between RDF types and Java classes.
 * 
 * @author Sebastian Schaffert
 */
@Entity
@PrimaryKeyJoinColumn(name="NODE_ID")
@DiscriminatorValue("LITERAL")
//@SecondaryTable( name = "KIWILITERAL")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@BatchSize(size = 100)
//@org.hibernate.annotations.Table(appliesTo="KIWILITERAL", optional=false)
@NamedQueries({
	// TODO: I am not sure whether it is safe to use the MD5 value here, this could lead to 
	//       problems in exceptional cases ...
	@NamedQuery(name  = "tripleStore.literalByValueLanguageType", 
			    query = "from KiWiLiteral l " +
			    		"where l.contentMd5  = :md5 " +
			    		"  and l.language = :lang " +
			    		"  and l.type     = :type"),
	@NamedQuery(name  = "tripleStore.literalByValueType", 
			    query = "from KiWiLiteral l " +
			    		"where l.contentMd5  = :md5 " +
			    		"  and l.language is null " +
			    		"  and l.type     = :type")
 })
public abstract class KiWiLiteral extends KiWiNode {

    /**
	 * 
	 */
	private static final long serialVersionUID = 1772323725671607249L;


    /**
     * An internal checksum of the content of the literal, used to efficiently check equality.
     */
	@Basic
	@NotNull
	@Index(name="idx_literal_content")
    @Column(length = 40)
//    @Column(table = "KIWILITERAL", length = 40)
	private String contentMd5;
	

	@Basic
    @Column(length = 5)
//    @Column(table = "KIWILITERAL", length = 5)
    private Locale language;
    
    @Basic
    @Column(length = 2048)
//    @Column(table = "KIWILITERAL", length = 2048)
    private String type;

    public KiWiLiteral() {
        super();
    }

    public KiWiLiteral(KiWiUser creator) {
        super(creator);
    }

    protected KiWiLiteral(Locale language, String type) {
        this();
        this.language = language;
        this.type = type;
    }

    public KiWiLiteral(String content, Locale language, String type) {
        this(language,type);
        setContent(content);
     }


     /**
     * Return the content of the literal, using the parametrized Java type
     * @return
     */
    public abstract String getContent();


    /**
     * Set the content of the literal to the content provided as parameter.
     * @param content
     */
    public abstract void setContent(String content);

    /**
     * Get the locale representing the language this literal is in; returns null 
     * if no language is associated with the literal
     * @return
     */
    public Locale getLanguage() {
        return language;
    }

    /**
     * Set the language of this literal to the given locale.
     * @param language
     */
    public void setLanguage(Locale language) {
        this.language = language;
    }

    /**
     * Return the RDF/XSD type of this literal.
     * @return
     */
    public String getType() {
        return type;
    }

    /**
     * Set the RDF/XSD type of this literal.
     * @param type
     */
    public void setType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        result.append("\""+getContent().toString()+"\"");
        if(language != null) {
            result.append("@"+language.getLanguage());
        }
        if(type != null) {
			result.append("^^"+getType().toString());
		}
        return result.toString();
    }

    /**
     * A separate equalsContent method for checking whether literals are equal in their content; we cannot override
     * the .equals function with this because it would break the system in cases where the same content is used in
     * different literals.
     *
     * @param o
     * @return
     */
    public boolean equals(Object o) {
        if (this == o) return true;
        
        if (Hibernate.getClass(this) != Hibernate.getClass(o)) {
            return false;
        }


        KiWiLiteral that = (KiWiLiteral) o;

        if (getContentMd5() != null ? !getContentMd5().equals(that.getContentMd5()) : that.getContentMd5() != null) return false;
        if (getLanguage() != null ? !getLanguage().equals(that.getLanguage()) : that.getLanguage() != null) return false;
        if (getType() != null ? !getType().equals(that.getType()) : that.getType() != null) return false;

        return true;
    }


    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + (this.getContentMd5() != null ? this.getContentMd5().hashCode() : 0);
        hash = 89 * hash + (this.getLanguage() != null ? this.getLanguage().hashCode() : 0);
        hash = 89 * hash + (this.getType() != null ? this.getType().hashCode() : 0);
        return hash;
    }

	/**
	 * @author
	 * @return
	 * @see KiWiNode#getCacheKey()
	 */
	@Override
	public String getCacheKey() {
        return TripleStoreUtil.createLiteralCacheKey(contentMd5,language,type);
	}



	@Override
	public boolean isAnonymousResource() {
		return false;
	}

	@Override
	public boolean isLiteral() {
		return true;
	}

	@Override
	public boolean isUriResource() {
		return false;
	}

	@Override
	public String getShortName() {
		return getContentMd5();
	}

	public String getContentMd5() {
		return contentMd5;
	}

	public void setContentMd5(String contentMd5) {
		this.contentMd5 = contentMd5;
	}

    /**
     * Return a representation of the node in TURTLE syntax.
     * <p/>
     * http://www.w3.org/TeamSubmission/turtle/
     *
     * @return
     */
    @Override
    public String toTurtle() {
        String s_lang = language != null ? "@" + language.getLanguage() : "";
        String s_type = type != null && !type.equals(Constants.NS_XSD+"string") ?
                        "^^<" + type +">" : "";

        String content = getContent();
        String s_content;

        if(content.contains("\n")) {
            s_content = "\"\"\"" + content + "\"\"\"";
        } else {
            s_content = "\"" + content + "\"";
        }

        return s_content + s_lang + s_type;
    }


    /**
     * Return a representation of the node in RDF/JSON syntax.
     * <p/>
     * http://n2.talis.com/wiki/RDF_JSON_Specification
     *
     * @param asObject indicates whether formatting should be as object (i.e. with additional type information)
     * @return
     */
    @Override
    public String toJSON(boolean asObject) {
        if(!asObject) {
            throw new IllegalArgumentException("literals can only be formatted as objects");
        }

        return "{ \"type\" : \"literal\"," +
                " \"value\" : \"" + getContent().replace("\"","\\\"") + "\"" +
                (language != null ? ", \"lang\" : \""+language.getLanguage() + "\"" : "") +
                (type != null && !type.equals(Constants.NS_XSD+"string") ?
                                    ", \"type\" : \""+type+"\"" : "") +
                " }";

    }



}
